package zap

import (
	"os"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

type LevelEnabler zapcore.LevelEnabler

type Logger struct {
	*zap.SugaredLogger
	lv *zap.AtomicLevel
}

func (lg *Logger) SetLevel(lv zapcore.Level) {
	lg.lv.SetLevel(lv)
}

func newLogger(config *Config) *Logger {
	zapOptions := make([]zap.Option, 0)
	zapOptions = append(zapOptions, zap.AddStacktrace(zap.PanicLevel))
	if !config.HideCaller {
		zapOptions = append(zapOptions, zap.AddCaller(), zap.AddCallerSkip(config.CallerSkip))
	}

	lv := zap.NewAtomicLevelAt(zapcore.InfoLevel)
	if err := lv.UnmarshalText([]byte(config.Level)); err != nil {
		panic(err)
	}
	cores := make([]zapcore.Core, 0, 1)
	isErr := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl >= zapcore.ErrorLevel && lv.Level() <= zapcore.ErrorLevel
	})
	isNotErr := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
		return lvl < zapcore.ErrorLevel && lv.Level() <= lvl
	})

	var wsOut, wsErr = zapcore.Lock(os.Stdout), zapcore.Lock(os.Stderr)
	var encoder = zapcore.NewConsoleEncoder(*config.EncoderConfig)
	cores = append(cores,
		zapcore.NewCore(encoder, wsErr, isErr),
		zapcore.NewCore(encoder, wsOut, isNotErr),
	)
	if config.File.Enable {
		ws := zapcore.AddSync(newFileSyncer(&config.File.FileConfig))
		encoder := zapcore.NewJSONEncoder(*config.EncoderConfig)
		cores = append(cores, zapcore.NewCore(encoder, ws, lv))
	}

	lg := zap.New(zapcore.NewTee(cores...), zapOptions...)
	return &Logger{
		SugaredLogger: lg.Sugar(),
		lv:            &lv,
	}
}
